package com.share.dymamic.configer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

/** 
* @author weigen.ye 
* @date 创建时间：2016年11月12日 上午8:57:04 
*
*/
public class CenterPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer implements InitializingBean{

	/**
	 * 配置中心指定文件目录
	 */
	private final static String WCC_CONFIG_PATH_KEY = "wcc.root.path";

	/**
	 * 缺省web配置中心文件
	 */
	private final static String WCC_DATA_DEFAULT_PATH = "/data/appdatas/wcc";

	/**
	 * 配置中心配置环境路径，用于指定配置中心服务器地址
	 */
	private final static String WCC_CONFIG_ENV_FILE_NAME = "config_env.ini";

	/**
	 * 配置中心指定文件目录
	 */
	private final static String WCC_CONFIG_SERVER_URL_KEY = "wcc.server.url";

	private final static String WCC_DATASOURCE_PREFIX_KEY = "datasource_";

	/**
	 * 配置中心默认应用路径
	 */
	private final static String WCC_CONFIG_DEFAULT_SERVER_URL_FOR_TEST = "http://127.0.0.1:8080";

	private final static String WCC_CONFIG_DEFAULT_SERVER_URL_FOR_STG = "http://127.0.0.1:8080";

	private final static String WCC_CONFIG_DEFAULT_SERVER_URL_FOR_PRODUCTION = "http://127.0.0.1:8080";

	private final static String WCC_CONFIG_DEFAULT_SERVER_URL = "http://127.0.0.1:8080";

	/**
	 * key:value
	 */
	private static Properties globalProperties = new Properties();
	
	/**
	 * key:applicationName
	 * value: 
	 *       key:locations对应的文件名
	 *       value：配置中心配置文件
	 */
	private static Map<String, Map<String, Properties>> globalPropertiesMap = new ConcurrentHashMap<>();
	
	/**
	 * key:variable name
	 * value:applicationName_fileName
	 */
	private static Map<String, String> keyPropertiesMap = new ConcurrentHashMap<>();
	
	/**
	 * 数据源配置文件
	 * key:数据源bean对应名称
	 * value:数据源配置文件
	 */
	private static Map<String, Properties> dataSourcePropertiesMap = new HashMap<String, Properties>();
	
	/**
	 * 服务名称
	 */
	private String applicationName;

	/**
	 * 要加载的配置文件列表
	 */
	private List<String> locations;

	private String ip;

	private String hostName;

	/**
	 * 是否过滤掉数据源配置文件，默认为true，单独将datasource_开头的数据源配置文件保存到内存中
	 */
	private Boolean ignoreDataSource = true;

	/**
	 * 配置中心文件所在机器的url
	 */
	private String configCenterServerUrl;

	@Override
	public void afterPropertiesSet() throws Exception {
		//configure wcc root path
		String wccRootPath = System.getProperty(WCC_CONFIG_PATH_KEY);
		if (StringUtils.isBlank(wccRootPath)) {
			wccRootPath = WCC_DATA_DEFAULT_PATH;
		}

		//初始化主机ip
		initHostInfo();

		//获取配置中心服务器url
		getConfigCenterServerUrl(wccRootPath);

		//从配置中心下配置文件
		loadPropertiesFromWcc(wccRootPath);
		
		//过滤xml文件，xml文件不允许下载
		filterXmlFiles();
		
		//初始化globalPropertiesMap，服务对应的每个配置文件文件名与配置的key-value
		Map<String, Properties> fileNamePropertiesMap = globalPropertiesMap.get(applicationName);
		if(MapUtils.isEmpty(fileNamePropertiesMap)){
			fileNamePropertiesMap = new ConcurrentHashMap<>();
			globalPropertiesMap.put(applicationName, fileNamePropertiesMap);
		}
		//按文件类型分组 
		List<Properties> propertiesList = splitProperties(wccRootPath, fileNamePropertiesMap);
		
		//set local properties
		super.setPropertiesArray(propertiesList.toArray(new Properties[propertiesList.size()]));
		
		//merge properties
		globalProperties.putAll(super.mergeProperties());
	}
	
	/**
	 *@description 过滤xml文件 
	 */
	private void filterXmlFiles(){
		for (int i = 0; i < locations.size(); i++) {
			if(locations.get(i).toLowerCase().endsWith(".xml")){
				locations.remove(i);
			}
		}
	}
	
	/**
	 *@description 按文件类型分组 
	 *@param wccRootPath
	 *@param fileNamePropertiesMap
	 *@return 
	 */
	private List<Properties> splitProperties(String wccRootPath, Map<String, Properties> fileNamePropertiesMap){
		List<Properties> propertiesList = new ArrayList<Properties>();
		for (int i = 0; i < locations.size(); i++) {
			Properties properties = buildProperties(wccRootPath, locations.get(i));
			if (ignoreDataSource && locations.get(i).toLowerCase().contains(WCC_DATASOURCE_PREFIX_KEY)) {// 单独处理数据库定义
				int index = locations.get(i).lastIndexOf(".");
				String dataSourceName = locations.get(i).substring(0, index);
				dataSourcePropertiesMap.put(dataSourceName, properties);
			} else {
				propertiesList.add(properties);
				//set the mapping between filename and properties
				fileNamePropertiesMap.put(locations.get(i), properties);
				checkSameKey(locations.get(i), properties);
			}
		}
		return propertiesList;
	}
	
	/**
	 *@description 校验相同的key 
	 *@param fileName
	 *@param properties
	 */
	@SuppressWarnings("rawtypes")
	private void checkSameKey(String fileName, Properties properties){
		for (Enumeration en = properties.propertyNames(); en.hasMoreElements();) {
			String key = (String) en.nextElement();
			String appNameFileName = keyPropertiesMap.get(key);
			if(StringUtils.isEmpty(appNameFileName)){
				keyPropertiesMap.put(key, applicationName + "_" + fileName);
			}else {
				System.err.println("Key[" + key + "] already exist. You can find it in [" + appNameFileName + "] and ["
						+ applicationName + "_" + fileName + "]");
			}
		}
	}
	
	/**
	 * 构建配置文件
	 * 
	 * @param wccRootPath
	 * @param location
	 * @return
	 */
	private Properties buildProperties(String wccRootPath, String location) {
		Properties props = new Properties();
		String propertiesLocation = wccRootPath + "/snapshot/" + applicationName + "/" + location;
		try {
			props.load(FileUtils.openInputStream(new File(propertiesLocation)));
		} catch (IOException e) {
			throw new IllegalArgumentException("load properties error:" + propertiesLocation, e);
		}
		return props;
	}

	/**
	 * 加载配置文件
	 * 
	 * @param wccRootPath
	 */
	private void loadPropertiesFromWcc(String wccRootPath) {
		// 直接建立到snapshot目录
		File wccRoot = new File(wccRootPath + "/snapshot");
		if (!wccRoot.exists()) {
			wccRoot.mkdirs();
		}
		for (String location : locations) {
			if (isLocalExist(wccRootPath, location)) {// 本地版本中存在，则直接copy到snapshot
				copyToSnapshot(wccRootPath, location);
			} else {// 远程获取
				saveRemoteFile(wccRootPath, location);
			}
		}
	}

	/**
	 * 拷贝local文件到snapshot
	 * 
	 * @param wccRootPath
	 * @param location
	 */
	private void copyToSnapshot(String wccRootPath, String location) {
		File srcFile = new File(wccRootPath + "/local/" + applicationName + "/" + location);
		File destFile = new File(wccRootPath + "/snapshot/" + applicationName + "/" + location);
		try {
			if (destFile.exists()) {
				destFile.delete();
			} else {
				if (!destFile.getParentFile().exists()) {
					destFile.getParentFile().mkdirs();
				}
			}
			FileUtils.copyFile(srcFile, destFile);
		} catch (IOException e) {
			throw new IllegalArgumentException("copy local properties error:" + applicationName + "/" + location, e);
		}
	}

	/**
	 * 判断本地文件是否存在
	 * 
	 * @param wccRootPath
	 * @param location
	 * @return
	 */
	private boolean isLocalExist(String wccRootPath, String location) {
		String propertiesLocation = wccRootPath + "/local/" + applicationName + "/" + location;
		if (new File(propertiesLocation).exists()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 远程配置中心获取配置文件，并保持到snapshot中
	 * 
	 * @param wccRootPath
	 * @param fileName
	 */
	private void saveRemoteFile(String wccRootPath, String fileName) {
		URL url;
		InputStream is;
		try {
			if(configCenterServerUrl!=null&&configCenterServerUrl.contains("127.0.0.1")){
				//TODO 
				is=getClass().getClassLoader().getResourceAsStream("config-center/"+fileName);
				if(is.available() < 1){
					throw new IllegalArgumentException("配置文件存在问题！");
				}
			}else{
				url = new URL(configCenterServerUrl + "/download?applicationName=" + applicationName + "&fileName=" + fileName);
				is = url.openStream();
				if (is.available() < 1) {
					throw new IllegalArgumentException("load remote properties error:" + applicationName + "/" + fileName);
				}
			}
			File parentPath = new File(wccRootPath + "/snapshot/" + applicationName);// +"/"
			if (!parentPath.exists()) {
				parentPath.mkdirs();
			}
			String propertiesLocation = wccRootPath + "/snapshot/" + applicationName + "/" + fileName;
			OutputStream os = new FileOutputStream(propertiesLocation);
			byte bf[] = new byte[1024];
			int length = 0;
			while ((length = is.read(bf, 0, 1024)) != -1) {
				os.write(bf, 0, length);
			}
			is.close();
			os.close();
		} catch (Exception e) {
			throw new IllegalArgumentException("load remote properties error:" + applicationName + "/" + fileName, e);
		}
	}

	/**
	 * 获取远程配置中心http url
	 * 
	 * @param wccRootPath
	 * @return
	 */
	private void getConfigCenterServerUrl(String wccRootPath) {
		/**
		 * 1、系统变量中是否存在配置中心服务器地址
		 */
		String configCenterServerUrl = System.getProperty(WCC_CONFIG_SERVER_URL_KEY);
		/**
		 * 2、从config_env.ini文件中获取
		 */
		if (configCenterServerUrl == null) {
			File wccConfigEnvFile = new File(wccRootPath + "/" + WCC_CONFIG_ENV_FILE_NAME);
			if (wccConfigEnvFile.exists()) {
				try {
					Properties prop = new Properties();
					InputStream is = new FileInputStream(wccConfigEnvFile);
					prop.load(is);
					is.close();
					configCenterServerUrl = prop.getProperty(WCC_CONFIG_SERVER_URL_KEY);
					if (configCenterServerUrl != null) {
						System.out.println("************************************************************");
						System.out.println("Load wcc server url from config_env.ini:" + configCenterServerUrl);
						System.out.println("************************************************************");
						System.setProperty(WCC_CONFIG_SERVER_URL_KEY, configCenterServerUrl);
					}
				} catch (IOException e) {
					System.out.println("load " + wccRootPath + "/" + WCC_CONFIG_ENV_FILE_NAME + " error:" + e);
				}
			}
		}
		/**
		 * 3、根据机器信息，判断是哪个环境的机器
		 */
		if (configCenterServerUrl == null) {
			configCenterServerUrl = getConfigCenterServerUrlByEnv();
			if (configCenterServerUrl != null) {
				System.setProperty(WCC_CONFIG_SERVER_URL_KEY, configCenterServerUrl);
			}
		}
		/**
		 * 4、没有则使用缺省WCC_CONFIG_DEFAULT_HOST
		 */
		if (configCenterServerUrl == null) {
			configCenterServerUrl = WCC_CONFIG_DEFAULT_SERVER_URL;
			System.out.println("************************************************************");
			System.out.println("Load wcc server url from WCC_CONFIG_DEFAULT_SERVER_URL:" + configCenterServerUrl);
			System.out.println("************************************************************");
			System.setProperty(WCC_CONFIG_SERVER_URL_KEY, configCenterServerUrl);
		}
		this.configCenterServerUrl = configCenterServerUrl;
	}

	private String getConfigCenterServerUrlByEnv() {
		
		System.out.println("Server Info: ip="+ip+" hostName="+hostName);
		
		if (ip != null && (ip.startsWith("192.") || ip.startsWith("172."))) {// 内网网环境
			System.out.println("***********************************************************************************");
			System.out.println("Load wcc server url from Server Info: ip="+ip+" hostName="+hostName+" (Test) : " + WCC_CONFIG_DEFAULT_SERVER_URL_FOR_TEST);
			System.out.println("***********************************************************************************");
			return WCC_CONFIG_DEFAULT_SERVER_URL_FOR_TEST;
		}
		if (hostName != null && hostName.indexOf("-online-") != -1) {//线上环境
			System.out.println("***********************************************************************************");
			System.out.println("Load wcc server url from Server Info: ip="+ip+" hostName="+hostName+" (Production) : " + WCC_CONFIG_DEFAULT_SERVER_URL_FOR_PRODUCTION);
			System.out.println("***********************************************************************************");
			return WCC_CONFIG_DEFAULT_SERVER_URL_FOR_PRODUCTION;
		} else if (hostName != null && hostName.indexOf("-pl-") != -1) {//预发布环境
			System.out.println("***********************************************************************************");
			System.out.println("Load wcc server url from Server Info: ip="+ip+" hostName="+hostName+" (Stging)  : " + WCC_CONFIG_DEFAULT_SERVER_URL_FOR_STG);
			System.out.println("***********************************************************************************");
			return WCC_CONFIG_DEFAULT_SERVER_URL_FOR_STG;
		}
		return null;

	}

	private void initHostInfo() {
		try {
			ip = InetAddress.getLocalHost().getHostAddress();
			hostName = InetAddress.getLocalHost().getHostName();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}

	}

	public List<String> getLocations() {
		return locations;
	}

	public void setLocations(List<String> locations) {
		this.locations = locations;
	}

	public static Properties getDataSourceProperties(String dataSourceName) {
		return dataSourcePropertiesMap.get(dataSourceName);
	}

	public Boolean getIgnoreDataSource() {
		return ignoreDataSource;
	}

	public void setIgnoreDataSource(Boolean ignoreDataSource) {
		this.ignoreDataSource = ignoreDataSource;
	}

	public void setConfigCenterServerUrl(String configCenterServerUrl) {
		this.configCenterServerUrl = configCenterServerUrl;
	}
	
	/**
	 *@description 根据key查找value
	 *@param key
	 *@return
	 */
	public static String getPropertyByKey(String key) {
		return globalProperties.getProperty(key);
	}

	/**
	 *@description 根据key查找value，传入默认值 
	 *@param key
	 *@param defaultValue
	 *@return
	 */
	public static String getPropertyByKeyWithDefaultValue(String key, String defaultValue) {
		return globalProperties.getProperty(key, defaultValue);
	}
	
	/**
	 *@description 根据applicationName,fileName,key查询value 
	 *@param applicationName
	 *@param fileName
	 *@param key
	 *@return
	 */
	public static String getProperty(String applicationName, String fileName, String key) {
		if(StringUtils.isEmpty(applicationName)){
			return "";
		}
		Map<String, Properties> fileNamePropertiesMap = globalPropertiesMap.get(applicationName);
		if(MapUtils.isEmpty(fileNamePropertiesMap)){
			return "";
		}
		Properties properties = fileNamePropertiesMap.get(fileName);
		if(properties == null){
			return "";
		}
		return properties.getProperty(key);
	}
	
	public String getApplicationName() {
		return applicationName;
	}

	public void setApplicationName(String applicationName) {
		this.applicationName = applicationName;
	}
	
	public static void main(String args[]){
		File file=new File("doc/config-center/application.properties");
		InputStream inputStream=null;
		if(file.exists()){
			try {
				inputStream=new FileInputStream(file);
				System.out.println("@@@@");
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
		try {
			if(inputStream.available()>1){
				System.out.println("@@@@");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
